#include "bib_Linea_Comando.h"

int esParametroValido(char *cadena, char *tipo);
int deLogicaAFisica(uint32_t direccionLogica, struct EST_CHS *chs);

#define NUMERICO	'n'
#define CS			'c'

extern struct	EST_CHS disco;
uint32_t		cantSectoresDisco;

int esParametroValido(char *cadena, char *tipo){

	char contador = 0;

	*tipo = NUMERICO;

	while (*cadena){
		if (*cadena == ':'){
			if (!contador){
				contador++;
				*tipo = CS;
			}
			else
				return FIN_ERROR;
		}

		else if (!isdigit(*cadena))
			return FIN_ERROR;
		cadena++;
	}

	return FIN_OK;
}
/*=====================================================================================*/
int deFisicaALogica(uint32_t *direccionLogica, struct EST_CHS chs){

	*direccionLogica = disco.sector * chs.cilindro + chs.sector;

	return FIN_OK;
}
/*=====================================================================================*/
int deLogicaAFisica(uint32_t direccionLogica, struct EST_CHS *chs){

	chs->cilindro = direccionLogica / disco.sector;
	chs->sector = direccionLogica % disco.sector;

	return FIN_OK;
}
/*=====================================================================================*/
int obtenerTamanioDisco(int sock){

	struct HEADER_NIPC header;
	struct EST_CHS *payload;

	if (recibirMensajeNIPCDinamico(sock, &header, (char **) &payload)){
		imprimirLog(ERROR, "No se pudo responder al comando info", "");
		return FIN_ERROR;
	}

	disco = *payload;
	free(payload);
	cantSectoresDisco = disco.cilindro * disco.sector;

	imprimirLogNumerico(DEBUG, "Cant. cilindros:", disco.cilindro);
	imprimirLogNumerico(DEBUG, "Cant. sectores:", disco.sector);

	return FIN_OK;
}
/*=====================================================================================*/
int obtenerComando(char linea[], char *comandoYParametros[]){

	char i;
	/*char tipo;*/

	fgets(linea, TAM_MAX_LINEA, stdin);

	if (!(*comandoYParametros = strtok(linea, " \n"))){
		imprimirLog(WARNING, "Solo ingreso espacios", "");
		return FIN_WARNING;
	}

	for(i = 1; i < (CANT_MAX_PARAMETROS + 1); i++)
		*(comandoYParametros + i) = strtok(NULL, " \n");

	if (strtok(NULL, " \n")){
		imprimirLogNumerico(WARNING, "Cant. de parametros invalida. La cant. de parametros debe ser menor o igual a", CANT_MAX_PARAMETROS);
		return FIN_WARNING;
	}

	/*for (i = 1; i < (CANT_MAX_PARAMETROS + 1) && *(comandoYParametros + i); i++)
		if (esParametroValido(*(comandoYParametros + i), &tipo)){
			imprimirLog(WARNING, "Los parametros deben ser numericos o del tipo <Nro. pista>:<Nro. sector>", "");
			return FIN_WARNING;
		}*/

	return FIN_OK;
}
/*=====================================================================================*/
int info(int sock, char *comandoYParametros[]){

	struct HEADER_NIPC header;
	struct EST_CHS *posicion;
	char cadena[20];

	if (comandoYParametros[1]){
		imprimirLog(WARNING, "El comando info no lleva parametros", "");
		return FIN_WARNING;
	}

	header.type = POSICION;
	header.payloadLength = 0;

	if (enviarMensajeNIPC(sock, &header, NULL)){
		imprimirLog(ERROR, "No se pudo enviar el comando info", "");
		return FIN_ERROR;
	}

	if (recibirMensajeNIPCDinamico(sock, &header, (char **) &posicion)){
		imprimirLog(ERROR, "No se pudo responder al comando info", "");
		return FIN_ERROR;
	}

	sprintf(cadena, "CHS(%d, %d, %d)", posicion->cilindro, posicion->cabezal, posicion->sector);
	free(posicion);

	imprimirLog(INFO, "Posicion actual:", cadena);

	return FIN_OK;
}
/*=====================================================================================*/
int clean(int sock, char *comandoYParametros[]){

	struct HEADER_NIPC header;
	struct PAYLOAD_ESCRITURA payload;
	char i;
	char *rta;
	uint32_t inicio;
	uint32_t fin;
	uint32_t cantPedidos;
	char tipo;

	if (!comandoYParametros[2] || comandoYParametros[3]){
		imprimirLog(WARNING, "El comando clean debe llevar solo 2 parametros", "");
		return FIN_WARNING;
	}

	for (i = 1; i < 3; i++){
		if (esParametroValido(*(comandoYParametros + i), &tipo)){
			imprimirLog(WARNING, "Los parametros de clean deben ser numericos", "");
			return FIN_WARNING;
		}
		if (tipo == CS){
			imprimirLog(WARNING, "Los parametros de clean deben ser numericos", "");
			return FIN_WARNING;
		}
	}

	inicio = atoi(*(comandoYParametros + 1));
	fin = atoi(*(comandoYParametros + 2));
	cantPedidos = fin - inicio + 1;

	if (inicio > fin){
		imprimirLog(WARNING, "El primer parametro no puede ser mayor al segundo", "");
		return FIN_WARNING;
	}

	if (fin >= cantSectoresDisco){
		imprimirLogNumerico(WARNING, "Los parametros de clean deben estar entre 0 y", cantSectoresDisco - 1);
		return FIN_WARNING;
	}

	header.type = ESCRITURA;
	header.payloadLength = sizeof(struct PAYLOAD_ESCRITURA);

	payload.id = 0;
	memset(payload.buffer, '\0', TAM_SECTOR);

	for(i = inicio; i <= fin; i++){

		payload.sector = i;

		if (enviarMensajeNIPC(sock, &header, (char *) &payload)){
			imprimirLogNumerico(ERROR, "No se pudo enviar el comando clean. Sector:", payload.sector);
			return FIN_ERROR;
		}
	}

	imprimirLogNumerico(DEBUG, "Pedidos enviados:", cantPedidos);

	for(i = 0; i < cantPedidos; i++){

		imprimirLogNumerico(DEBUG, "Nro. de vueltas:", i);

		if (recibirMensajeNIPCDinamico(sock, &header, &rta)){
			imprimirLog(ERROR, "No se pudo responder al comando clean", "");
			return FIN_ERROR;
		}

		free(rta);

		if (header.type == RTA_ERROR){
			imprimirLog(ERROR, "Ocurrio un error de escritura", "");
			return FIN_ERROR;
		}
	}

	imprimirLog(INFO, "clean OK", "");

	return FIN_OK;
}
/*=====================================================================================*/
int trace(int sock, char *comandoYParametros[]){

	struct HEADER_NIPC header;
	/*uint32_t sector;*/
	char i;
	uint16_t j;
	char cantPedidos;
	char *respuesta;
	struct EST_PLANIFICACION planif;
	uint32_t *sectoresRecorridos;
	struct EST_CHS direccionFisica;
	char *cadena;
	char tipo;
	struct EST_CHS dirFisica;
	uint32_t dirLogica;
	char cad[10];
	char cantPedidosAnulados;

	if (!comandoYParametros[1]){
		imprimirLog(WARNING, "El comando trace debe llevar al menos un parametro", "");
		return FIN_WARNING;
	}

	header.type = PRUEBA;
	header.payloadLength = sizeof(uint32_t);

	cantPedidosAnulados = 0;
	for(i = 1; *(comandoYParametros + i); i++){


		if (esParametroValido(*(comandoYParametros + i), &tipo)){
			imprimirLog(WARNING, "Los parametros de clean deben ser del tipo <Nro. pista>:<Nro. sector>. Parametro anulado:", *(comandoYParametros + i));
			imprimirLogNumerico(DEBUG, "1", tipo);
			cantPedidosAnulados++;
		}

		else if (tipo == NUMERICO){
			imprimirLog(WARNING, "Los parametros de clean deben ser del tipo <Nro. pista>:<Nro. sector>. Parametro anulado:", *(comandoYParametros + i));
			imprimirLogNumerico(DEBUG, "2", tipo);
			cantPedidosAnulados++;
		}

		else{
			strcpy(cad, *(comandoYParametros + i));

			dirFisica.cilindro = atoi(strtok(*(comandoYParametros + i), ":"));
			if (dirFisica.cilindro >= disco.cilindro){
				imprimirLog(WARNING, "Nro. de cilindro erroneo. Parametro anulado:", cad);
				cantPedidosAnulados++;
			}

			else{
				dirFisica.sector = atoi(strtok(NULL, ":"));
				if (dirFisica.sector >= disco.sector){
					imprimirLog(WARNING, "Nro. de sector erroneo. Parametro anulado:", cad);
					cantPedidosAnulados++;
				}
				/*sector = atoi(*(comandoYParametros + i));

				imprimirLogNumerico(DEBUG, "Sector solicitado para trace:", sector);*/

				deFisicaALogica(&dirLogica, dirFisica);

				if (enviarMensajeNIPC(sock, &header, (char *) &dirLogica)){
					imprimirLog(ERROR, "No se pudo enviar el comando trace. Sector:", cad);
					return FIN_ERROR;
				}
			}
		}

		cantPedidos = i;
	}

	cantPedidos -= cantPedidosAnulados;

	imprimirLogNumerico(DEBUG, "Cant. pedidos:", cantPedidos);
	for(i = 0; i < cantPedidos; i++){

		char cadenaAux[50];

		imprimirLog(DEBUG, "Estoy vivo 1", "");

		if (recibirMensajeNIPCDinamico(sock, &header, &respuesta)){
			imprimirLog(ERROR, "No se pudo responder al comando trace", "");
			return FIN_ERROR;
		}

		imprimirLog(DEBUG, "Estoy vivo 4", "");

		imprimirLogNumerico(DEBUG, "Respuesta recibida, nro.", i);

		memcpy((char *) &planif, respuesta, sizeof(struct EST_PLANIFICACION));
		sectoresRecorridos = (uint32_t *) malloc(sizeof(uint32_t) * planif.cantSectoresRecorridos);
		memcpy((char *)sectoresRecorridos, respuesta + sizeof(struct EST_PLANIFICACION), sizeof(uint32_t) * planif.cantSectoresRecorridos);

		/*for(i = 0; i < 36; i++){
			printf("%d\n", respuesta[i]);
		}
		memcpy(aux, (char *) sectoresRecorridos, sizeof(uint32_t) * planif.cantSectoresRecorridos);
		for(i = 0; i < 12; i++){
			printf("aux %d: %d\n", i, aux[i]);
		}
		for(i = 0; i < planif.cantSectoresRecorridos; i++)
			printf("Dir. Logica: %d\n", sectoresRecorridos[i]);*/

		free(respuesta);

		if (planif.cantSectoresRecorridos > 1)
			cadena = (char *) malloc(11 * planif.cantSectoresRecorridos);
		else
			cadena = (char *) malloc(12);

		deLogicaAFisica(planif.posicionActual, &direccionFisica);
		sprintf(cadena, "%d:%d", direccionFisica.cilindro, direccionFisica.sector);
		imprimirLog(INFO, "Posicion Actual:", cadena);

		deLogicaAFisica(planif.sectorSolicitado, &direccionFisica);
		sprintf(cadena, "%d:%d", direccionFisica.cilindro, direccionFisica.sector);
		imprimirLog(INFO, "Sector solicitado:", cadena);

		if (planif.cantSectoresRecorridos){
			strcpy(cadena, "");
			for(j = 0; j < planif.cantSectoresRecorridos; j++){
				/*imprimirLogNumerico(DEBUG, "Dir. Logica:", *(sectoresRecorridos + i * sizeof(uint32_t)));*/
				deLogicaAFisica(*(sectoresRecorridos + j), &direccionFisica);
				sprintf(cadena, "%s%d:%d, ", cadena, direccionFisica.cilindro, direccionFisica.sector);
			}
			free(sectoresRecorridos);
			*(cadena + strlen(cadena) - 2) = '\0';
		}
		else
			strcpy(cadena, "-");
		imprimirLog(INFO, "Sectores Recorridos:", cadena);

		sprintf(cadenaAux, "Tiempo consumido [ms]: %f", planif.tiempoConsumido);
		imprimirLog(INFO, cadenaAux, "");
		/*imprimirLogNumerico(INFO, "Tiempo consumido:", planif.tiempoConsumido);*/

		if (planif.tamanioCola > 1){
			deLogicaAFisica(planif.proximoSector, &direccionFisica);
			sprintf(cadena, "%d:%d", direccionFisica.cilindro, direccionFisica.sector);
		}
		else
			strcpy(cadena, "-");
		imprimirLog(INFO, "Proximo sector:", cadena);

		free(cadena);
	}

	imprimirLog(INFO, "trace OK", "");

	return FIN_OK;
}
/*=====================================================================================*/
int salir(int sock, char *comandoYParametros[], char *flagFin){

	struct HEADER_NIPC header;

	if (comandoYParametros[1]){
		imprimirLog(WARNING, "El comando salir no lleva parametros", "");
		return FIN_WARNING;
	}

	header.type = SALIR;
	header.payloadLength = 0;

	if (enviarMensajeNIPC(sock, &header, NULL)){
		imprimirLog(ERROR, "No se pudo enviar el comando salir", "");
		return FIN_ERROR;
	}

	*flagFin = 0;

	return FIN_OK;
}
